/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.nys.conci.base.componentes.seguridad;

import com.nys.conci.base.excepcion.ExcepcionServicio;
import com.nys.conci.base.utilitario.logger.LoggerUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.HashPrintServiceAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.PrintServiceAttributeSet;
import javax.print.attribute.standard.MediaSizeName;
import javax.servlet.ServletContext;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.*;
import net.sf.jasperreports.engine.util.JRLoader;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;

/**
 *
 * @author Yeudy10
 */
public class ReporteControlador {
  private String nombre_archivo = "";
    private FormatoExportar formato_exportar;
    private StreamedContent contenido;
    private Boolean imprimir;

    /**
     * Enumerado con un listado de todas las plantillas utilizadas para
     * reportes, cada valor, contiene la direccion de dicha plantilla.
     */
    public enum Plantilla {

        REPORTE_LISTADO_USUARIOS("Listado de Usuarios",
        "/WEB-INF/reporte/usuarios/listadoUsuarios.jasper");
        public final String ruta;
        public final String nombre_reporte;

        Plantilla(String nombre, String ruta) {
            this.ruta = ruta;
            this.nombre_reporte = nombre;
        }
    }

    /**
     * Listado de los formatos en los que se tiene soporte para generar los
     * distintos reportes.
     */
    public enum FormatoExportar {

        PDF("PDF", "pdf", "application/pdf"),
        EXCEL("Excel", "xls", "application/xls"),
        RTF("RFT", "rtf", "application/rtf");
        public final String formato;
        public final String extension;
        public final String mimeType;

        FormatoExportar(String f, String e, String m) {
            this.formato = f;
            this.extension = e;
            this.mimeType = m;
        }
    }

    //<<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
    //<<>><<>><<>><<>><<>><<>><<>>  METODOS   <<>><<>><<>><<>><<>><<>><<>><<>>
    //<<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
    /**
     * Este metodo obtiene una dirección URL relativa en el proyecto y la
     * convierte en una direccion Absoluta, tomando en cuenta el contexto y
     * ubicacion de la aplicacion.
     *
     * @param url Direccion url relativa al proyecto
     * @return Direccion URL de absoluta, tomando en cuenta al proyecto
     */
    public String ConvertirRutas(String url) {
        String resultado = "";
        try {
            FacesContext tempFacesContext = FacesContext.getCurrentInstance();
            ServletContext tempServletContext = (ServletContext) tempFacesContext.getExternalContext().getContext();
            resultado = tempServletContext.getRealPath(url);
        } catch (Exception e) {
            LoggerUtil.registrarLogError(e, true);
        }
        return resultado;
    }

    /**
     * Método encargado de la generacion del reporte especificado en sus
     * parámetros.Con este método se genera el reporte especificado en el
     * parámetro plantilla, dicha plantilla va a generar el reporte tomando como
     * DataSource la lista que recibe como parámetro. En resumen, que no
     * ejecutan una sentencia de SQL para tomar los datos a desplegar, si no que
     * los reciben por medio de la lista..
     *
     * @param plantilla Plantilla
     * @param ruta Plantilla del reporte, Ruta de la plantilla *.jasper, a la
     * cual generar el reporte
     * @param formato_exportar Formato del reporte a generar.
     * @param parametros Parametros que requiere el reporte.
     * @param conexion Conexion con la base de datos utilizada por el reporte.
     * @throws JRException
     */
    public void generarReporte(Plantilla plantilla, FormatoExportar formato_exportar,
            Map parametros, List datos) throws JRException {

        JasperReport reporte = (JasperReport) JRLoader.loadObject(this.ConvertirRutas(plantilla.ruta));
        JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, parametros, new JRBeanCollectionDataSource(datos));
        this.formato_exportar = formato_exportar;
        this.nombre_archivo = plantilla.nombre_reporte +"."+formato_exportar.extension;
        this.exportar(jasperPrint);
    }

    /**
     * Método encargado de la generación del reporte especificado en sus
     * parámetros. Con este método se genera el reporte especificado en el
     * parámetro plantilla, dicha plantilla va a generar el reporte tomando como
     * DataSource la conexión a la base de datos. En resumen, es para reportes
     * que ejecutan una sentencia para mostrar datos.
     *
     * @param plantilla Plantilla
     * @param ruta Plantilla del reporte, Ruta de la plantilla *.jasper, a la
     * cual generar el reporte
     * @param formato_exportar Formato del reporte a generar.
     * @param parametros Parametros que requiere el reporte.
     * @param conexion Conexion con la base de datos utilizada por el reporte.
     * @throws JRException
     */
    public void generarReporte(Plantilla plantilla, FormatoExportar formato_exportar,
            Map parametros, Connection conexion) throws JRException {
        try{
            JasperReport reporte = (JasperReport) JRLoader.loadObject(this.ConvertirRutas(plantilla.ruta));
            JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, parametros, conexion);
            this.formato_exportar = formato_exportar;
            this.nombre_archivo = plantilla.nombre_reporte +"."+formato_exportar.extension;
            if (imprimir != null && imprimir) {
                this.imprimir(jasperPrint);
            } else {
                this.exportar(jasperPrint);
            }
        }catch(Exception ex){
            throw new ExcepcionServicio("Ha ocurrido un error durante la generación del reporte.",
                    "Ha ocurrido un error durante la generación del reporte. ",
                    ex, getClass());
        }finally{
            this.cerrarConexion(conexion);
        }
    }
    
    

    /**
     * Metodo encargado de la generacion del reporte especificado en sus
     * parametros.
     *
     * @param plantilla Plantilla
     * @param ruta Plantilla del reporte, Ruta de la plantilla *.jasper, a la
     * cual generar el reporte
     * @param formato_exportar Formato del reporte a generar.
     * @param parametros Parametros que requiere el reporte.
     * @param conexion Conexion con la base de datos utilizada por el reporte.
     * @throws JRException
     */
    public byte[] generarReporteDirecto(Plantilla plantilla,
            Map parametros, Connection conexion) throws JRException {
        try{
            JasperReport reporte = (JasperReport) JRLoader.loadObject(this.ConvertirRutas(plantilla.ruta));
            JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, parametros, conexion);
            return imprimirDirecto(jasperPrint);
        }catch(Exception ex){
            throw new ExcepcionServicio("Ha ocurrido un error durante la generación directa del reporte.",
                        "Ha ocurrido un error durante la generación directa del reporte. ",
                        ex, getClass());
        }finally{
            this.cerrarConexion(conexion);
        }
    }

    /**
     * Este es el metodo encarga de la logica de la generacion. Encontrar el
     * formato en que se generara el reporte, crear el exporter indicado para
     * cada tipo de formato, realizar las configuraciones para cada formato.
     * Enviar todos los datos necesarios y finalmente generar el reporte.
     *
     * @param jp JasperPrint con la plantilla, conexion, parametros y demas.
     */
    private void exportar(JasperPrint jp) {
        try {
            JRExporter exporter = null;

            if (this.formato_exportar.equals(FormatoExportar.PDF)) {
                exporter = new JRPdfExporter();
            } else if (this.formato_exportar.equals(FormatoExportar.EXCEL)) {
                exporter = new JExcelApiExporter();
                exporter.setParameter(JExcelApiExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
                exporter.setParameter(JExcelApiExporterParameter.IS_AUTO_DETECT_CELL_TYPE, Boolean.TRUE);
                exporter.setParameter(JExcelApiExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_COLUMNS, Boolean.TRUE);
                //exporter.setParameter(JExcelApiExporterParameter.IS_IGNORE_CELL_BACKGROUND,Boolean.FALSE);
                exporter.setParameter(JExcelApiExporterParameter.IS_IGNORE_CELL_BORDER, Boolean.FALSE);
            } else if (this.formato_exportar.equals(FormatoExportar.RTF)) {
                exporter = new JRRtfExporter();
            }

            //HttpServletResponse response = (HttpServletResponse) econtext.getResponse();
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp);
            exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, os);
            exporter.exportReport();

            ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
            this.contenido = new DefaultStreamedContent(is, FormatoExportar.PDF.mimeType, this.nombre_archivo);

        } catch (Exception ex) {
            throw new RuntimeException("Error en la generación del reporte", ex);
        }
    }

    /**
     * Obtiene del objeto de impresión de JasperReport, el Arreglo de Bytes en Stream
     * para generar a partir de el, el archivo con el respectivo reporte generado.
     * @param jp Objeto de Impresión de Jasper
     * @return InputStream con los datos del archivo generado.
     */
    private ByteArrayInputStream exportarByte(JasperPrint jp) {
        try {

            JRExporter exporter = null;

            if (this.formato_exportar.equals(FormatoExportar.PDF)) {
                exporter = new JRPdfExporter();
            } else if (this.formato_exportar.equals(FormatoExportar.EXCEL)) {
                exporter = new JExcelApiExporter();
                exporter.setParameter(JExcelApiExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
                exporter.setParameter(JExcelApiExporterParameter.IS_AUTO_DETECT_CELL_TYPE, Boolean.TRUE);
                exporter.setParameter(JExcelApiExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_COLUMNS, Boolean.TRUE);
                //exporter.setParameter(JExcelApiExporterParameter.IS_IGNORE_CELL_BACKGROUND,Boolean.FALSE);
                exporter.setParameter(JExcelApiExporterParameter.IS_IGNORE_CELL_BORDER, Boolean.FALSE);
            } else if (this.formato_exportar.equals(FormatoExportar.RTF)) {
                exporter = new JRRtfExporter();
            }

            //HttpServletResponse response = (HttpServletResponse) econtext.getResponse();
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp);
            exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, os);
            exporter.exportReport();

            return new ByteArrayInputStream(os.toByteArray());


        } catch (Exception ex) {
            throw new RuntimeException("Error en la generación del reporte", ex);
        }
    }

    /**
     * Asignar ByteArrayInputStream al contenido del reporte
     *
     * @param is
     */
    public void asignarContenido(ByteArrayInputStream is) {
        try {

            this.contenido = new DefaultStreamedContent(is, FormatoExportar.PDF.mimeType, this.nombre_archivo);
        } catch (Exception ex) {
            throw new RuntimeException("Error en la generación del reporte", ex);
        }
    }

    
    /**
     * Retorna una cadena ByteArrayInputStream, del reporte generado
     *
     * @param jp
     * @return
     */
    public ByteArrayInputStream generarReporteBytes(Plantilla plantilla, FormatoExportar formato_exportar,
            Map parametros, Connection conexion) throws JRException {


        JasperReport reporte = (JasperReport) JRLoader.loadObject(this.ConvertirRutas(plantilla.ruta));
        JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, parametros, conexion);
        this.formato_exportar = formato_exportar;
        this.nombre_archivo = plantilla.nombre_reporte +"."+formato_exportar.extension;

        return this.exportarByte(jasperPrint);

    }

    /**
     * Este es el metodo encarga de la logica de la generacion. Encontrar el
     * formato en que se generara el reporte, crear el exporter indicado para
     * cada tipo de formato, realizar las configuraciones para cada formato.
     * Enviar todos los datos necesarios y finalmente generar el reporte.
     *
     * @param jp JasperPrint con la plantilla, conexion, parametros y demas.
     */
    private byte[] imprimirDirecto(JasperPrint jp) {
        try {
            JRExporter exporter;
            exporter = new JRPdfExporter();


            //HttpServletResponse response = (HttpServletResponse) econtext.getResponse();
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp);
            exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, os);
            exporter.exportReport();

            ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
            // fcontext.getApplication().getStateManager().saveView(fcontext);
            //fcontext.responseComplete();
            InputStream file = new DefaultStreamedContent(is, FormatoExportar.PDF.mimeType, this.nombre_archivo).getStream();
            byte[] archivoPDF = new byte[file.available()];
            file.read(archivoPDF);
            return archivoPDF;
        } catch (Exception ex) {
            throw new RuntimeException("Error en la generación del reporte", ex);
        }
    }

    /**
     * Imprime de forma directa un reporte. <BR/> Define los atributos de
     * impresión, crea un servicio de exportación de impresión de JasperPrints y
     * por ultimo imprime el reporte directamente en la impresora por defecto
     * del cliente. <BR/> Se debe setear el atributo imprimir en true para que
     * el mismo se imprima
     *
     * @param jasperPrint JasperPrint con la plantilla, conexion y parametros.
     * @author wporras
     * @since 08/08/2012
     */
    private void imprimir(JasperPrint jasperPrint) {
        try {
            //Atributos de impresión
            PrintRequestAttributeSet printRequestAttributeSet = new HashPrintRequestAttributeSet();
            printRequestAttributeSet.add(MediaSizeName.NA_LETTER);
//            printRequestAttributeSet.add(new PrinterName("PDFCreator", null));

            PrintServiceAttributeSet printServiceAttributeSet = new HashPrintServiceAttributeSet();

            //Exportador del servicio de impresión
            JRPrintServiceExporter exporter = new JRPrintServiceExporter();
            exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
            exporter.setParameter(JRPrintServiceExporterParameter.PRINT_REQUEST_ATTRIBUTE_SET, printRequestAttributeSet);
            exporter.setParameter(JRPrintServiceExporterParameter.PRINT_SERVICE_ATTRIBUTE_SET, printServiceAttributeSet);
            exporter.setParameter(JRPrintServiceExporterParameter.DISPLAY_PAGE_DIALOG, Boolean.FALSE);
            exporter.setParameter(JRPrintServiceExporterParameter.DISPLAY_PRINT_DIALOG, Boolean.FALSE);

            exporter.exportReport();
        } catch (Exception ex) {
            throw new RuntimeException("Error en la impresión del reporte", ex);
        }
    }

    /**
     * Obtiene el listado de items con los formatos en que se puede generar un
     * reporte
     *
     * @return Listado de items
     */
    public List<SelectItem> getItemsTiposExportar() {
        List<SelectItem> formatos = new ArrayList<SelectItem>();

        for (FormatoExportar fe : FormatoExportar.values()) {
            formatos.add(new SelectItem(fe.formato, fe.formato));
        }
        return formatos;
    }

    /**
     * Devuelve en nombre completo del reporte a generar, es decir, nombre mas
     * extension.
     *
     * @return Nombre completo de reporte.
     */
    public String getNombreCompletoReporte() {
        return this.nombre_archivo + "." + formato_exportar.extension;
    }
    
    
    /**
     * Método encargado de cerrar la conexión utilizada por la generación de los 
     * reportes.
     * 
     * @param conexion Conexión a la base de datos
     */
    private void cerrarConexion(Connection conexion){
        try {
            conexion.close();
        } catch (SQLException ex) {
            LoggerUtil.registrarLogError("Ha ocurrido un error cerrando la conexión "+
                    "al terminar la generación del reporte", 
                    true);
        }
    }

    //<<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
    //<<>><<>><<>><<>><<>><<>>   GETTERS y SETTERS    <<>><<>><<>><<>><<>><<>>
    //<<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
    public FormatoExportar getFormatoPDF() {
        return this.formato_exportar.PDF;
    }

    public FormatoExportar getFormatoXLS() {
        return this.formato_exportar.EXCEL;
    }

    public FormatoExportar getFormatoRTF() {
        return this.formato_exportar.RTF;
    }

    public String getFormatoActual() {
        return this.formato_exportar.formato;
    }

    public StreamedContent getContenido() {
        DefaultStreamedContent dsc = null;
        if (this.contenido != null) {
            dsc = new DefaultStreamedContent();
            dsc.setStream(this.contenido.getStream());
            dsc.setContentType(this.contenido.getName());
            dsc.setName(this.contenido.getName());
            contenido = null;
        }
        return dsc;
    }

    /**
     * @return the imprimir
     */
    public Boolean getImprimir() {
        return imprimir;
    }

    /**
     * @param imprimir the imprimir to set
     */
    public void setImprimir(Boolean imprimir) {
        this.imprimir = imprimir;
    }
}
